#include <nice2d/tostring.h>
#include <nice2d/application.h>
#include <nice2d/components/fontmanager.h>
#include <nice2d/widgets/desktop.h>
#include <nice2d/widgets/button.h>

IMPLEMENT_OBJECT(ButtonStyle, BasicButtonStyle)

ButtonStyle::ButtonStyle()
{
}

ButtonStyle::~ButtonStyle()
{
}

PTree ButtonStyle::serialize()
{
    PTree node;
    node.add("hMode", toString(hMode));
    node.add("vMode", toString(vMode));
    node.add("style", toString(style));
    node.add_child("textColor", serializeObject(textColor));
    return node;
}

void ButtonStyle::deserialize(const PTree &node)
{
    fontFile = node.get<std::string>("fontFile");
    characterSize = node.get<int32_t>("characterSize");
    hMode = fromString(node.get<std::string>("hMode"), HMode_Center);
    vMode = fromString(node.get<std::string>("vMode"), VMode_Center);
    style = fromString(node.get<std::string>("style"), sf::Text::Style::Regular);
    textColor = deserializeColor(node.get_child("textColor"));
}

void ButtonStyle::sync()
{
}

class ButtonData
{
public:
    ButtonData(Button *thisButton):
        button(thisButton)
    {
        text = std::make_shared<sf::Text>();

        auto desktop = GET_COMPONENT(Application::getInstance(), Desktop);
        auto style = std::dynamic_pointer_cast<ButtonStyle>(desktop->getControlStyle("Button"));
        button->setControlStyle(style);
        applySyle();
    }

    void applySyle()
    {
        auto fontManager = GET_COMPONENT(Application::getInstance(), FontManager);

        auto style = button->getTemplateStyle<ButtonStyle>();
        auto font = fontManager->loadFromFile(style->fontFile);

        text->setFont(*font);
        text->setCharacterSize(style->characterSize);
        text->setStyle(style->style);
        text->setFillColor(style->textColor);

        button->setBackground(style->normalBackground);
    }

    Button *button;
    std::shared_ptr<sf::Text> text;
};

IMPLEMENT_OBJECT(Button, BasicButton)

Button::Button(const sf::Vector2f &size):
    BasicButton(size),
    data(new ButtonData(this))
{
}

Button::~Button()
{
}

void Button::setText(const sf::String &text)
{
    if (text != data->text->getString()) {
        data->text->setString(text);
        onPositionChanged();
    }
}

sf::String Button::getText() const
{
    return data->text->getString();
}

void Button::onPositionChanged()
{
    recalcTextPosition(0, 0);
}

void Button::onButtonStateChanged()
{
    auto state = getButtonState();
    if (state == BasicButtonState_Down)
        recalcTextPosition(1.0f, 1.0f);
    else
        recalcTextPosition(0.0f, 0.0f);
}

void Button::recalcTextPosition(float offsetx, float offsety)
{
    auto style = getTemplateStyle<ButtonStyle>();
    auto textBox = data->text->getLocalBounds();
    auto position = Entity::adjustPosition(getBoundingBox(), sf::Vector2f(textBox.width,
                                                                          textBox.height),
                                           HMode_Center,
                                           VMode_Center, getPadding(), getPadding());

    auto box = getBoundingBox();
    data->text->setPosition(position.x - box.left + offsetx,
                            position.y - box.top - textBox.top + offsety);
}

void Button::onDrawControl(sf::RenderTarget &target, sf::RenderStates states) const
{
    BasicButton::onDrawControl(target, states);
    target.draw(*data->text.get(), states);
}

void Button::onStyleChanged()
{
    BasicButton::onStyleChanged();
    data->applySyle();
}
